/*
 *  network.cpp
 *  
 *  Created by James Dykstra
 *	Team 5
 *
 *  Copyright 2010 Michigan Technological University. All rights reserved.
 */

#include "network.h"

Network::Network(const char *i)
{
	std::fstream log("log.txt", std::fstream::out | std::fstream::app);
	log<<"Initializing Network: ";

	if(SDLNet_Init()==-1)
	{
		log<<"SDLNet_Init failed: "<<SDLNet_GetError()<<std::endl;
		active = false;
	}
	else
		active = true;
	
	ip = (char *) i;
	if(ip == NULL)
	{
		server = true;
		clientNum = 0;
		cur = 0;
		log<<"   This client acting as network server.\n";
	}
	else
	{
		server = false;
		clientNum = -1;
		cur = -1;
		log<<"   This client acting as network client.\n";
	}
	for(int i=0; i<MAX_CLIENTS; i++)
	{
		opp[i].host = 0;
		opp[i].port = 0;
		minions[i] = NULL;
	}
	
	if(active && !server)
	{
		if(SDLNet_ResolveHost(&opp[0], ip, 9864)==-1)
		{
			log<<"SDLNet_ResolveHost failed: "<<SDLNet_GetError()<<std::endl;
			active = false;
		}
		else
			log<<"                         Done.\n";
	}
	else
		log<<"                         Done.\n";

	log.close();
	servSock = NULL;
	sendSock = NULL;
	packet = NULL;
	player = NULL;
	sound = NULL;
	powerup = NULL;
}
Network::~Network()
{
	if(!servSock)
		SDLNet_UDP_Close(servSock);
	
	if(!sendSock)
		SDLNet_UDP_Close(sendSock);

	SDLNet_Quit();
}
bool Network::open()
{
	std::fstream log("log.txt", std::fstream::out | std::fstream::app);
	log<<"Opening Socket: ";

	if(!active)
	{
		log<<"Network inactive - Failed to open socket.\n";
		log.close();

		return false;
	}
	else
	{
		if(server)
		{
			servSock = SDLNet_UDP_Open(9864);
			sendSock = SDLNet_UDP_Open(0);
		}
		else
			servSock = SDLNet_UDP_Open(0);

		if(!servSock || (server && !sendSock))
		{
			log<<"Opening server socket failed - ";
			log<<"SDLNet_UDP_Open: "<<SDLNet_GetError()<<std::endl;
			log.close();

			return false;
		}
		else if(!server)
			cur = 0;
	}

	log<<"         Done.\n";
	log.close();

	return true;
}
std::string Network::extractData(UDPpacket *p)
{
	std::string retStr = "";
	if(p != NULL)
	{
		retStr = (char*)p->data;
		retStr = retStr.substr(0, p->len);

		return retStr;
	}
	else
		return "";
}
UDPpacket *Network::recvPacket()
{
	std::fstream log("log.txt", std::fstream::out | std::fstream::app);

	if(!servSock)
	{
		log<<"Server socket not open!\nFailed to listen.\n";
		log.close();

		return NULL;
	}
	log.close();

	bool run = true;
	packet = SDLNet_AllocPacket(512);
	while(run)
	{
		if(!servSock)
			run = false;
		else
		{
			if(SDLNet_UDP_Recv(servSock, packet))
				return packet;
		}
	}

	SDLNet_FreePacket(packet);

	return NULL;
}
//Send for network server
//Gives option to add ID to data and to exclude a client when sending data to all
bool Network::sendAll(std::string data, int excn, bool addId)
{
	std::fstream log("log.txt", std::fstream::out | std::fstream::app);

	if(!sendSock)
	{
		log<<"Socket not connected!\nFailed to send data.\n";
		log.close();
		return false;
	}
	log.close();

	UDPpacket *p = NULL;
	if(addId)
	{
		std::string temp = "P:";
		temp += ProtocolParser::numToStr(clientNum);
		temp += data;
			
		p = SDLNet_AllocPacket(temp.length());
		memcpy(p->data, temp.c_str(), temp.length());
		p->len = temp.length();
	}
	else
	{
		p = SDLNet_AllocPacket(data.length());
		memcpy(p->data, data.c_str(), data.length());
		p->len = data.length();
	}

	for(int i=0; i<MAX_CLIENTS; i++)
	{
		if(minions[i]->getClientNum() != excn && minions[i]->getClientNum() != -1)
		{
			p->address = opp[i];
			
			if(server)
				SDLNet_UDP_Send(sendSock, -1, p);
			else
				SDLNet_UDP_Send(servSock, -1, p);
		}
	}

	SDLNet_FreePacket(p);

	return true;
}
//Send for network clients
bool Network::send(std::string data)
{
	return send(data, 0, true);
}
bool Network::send(std::string data, int cn, bool addId)
{
	std::fstream log("log.txt", std::fstream::out | std::fstream::app);

	if(!servSock || (server && !sendSock))
	{
		log<<"Socket not connected!\nFailed to send data.\n";
		log.close();
		return false;
	}

	UDPpacket *p = NULL;
	if(addId)
	{
		std::string temp = "P:";
		temp += ProtocolParser::numToStr(clientNum);
		temp += data;
			
		p = SDLNet_AllocPacket(temp.length());
		memcpy(p->data, temp.c_str(), temp.length());
		p->len = temp.length();
	}
	else
	{
		p = SDLNet_AllocPacket(data.length());
		memcpy(p->data, data.c_str(), data.length());
		p->len = data.length();
	}

	p->address = opp[cn];

	if(server)
		SDLNet_UDP_Send(sendSock, -1, p);
	else
		SDLNet_UDP_Send(servSock, -1, p);

	SDLNet_FreePacket(p);

	return true;
}
void Network::setOpponents(Opponent *c[MAX_CLIENTS])
{
	for(int i=0; i<MAX_CLIENTS; i++)
		minions[i] = c[i];
}
bool Network::newPlayerConnected(UDPpacket *p)
{
	if((cur+1) > MAX_CLIENTS)
		return false;
	else
		cur++;

	opp[(cur-1)] = p->address;
	SDLNet_FreePacket(p);
	
	minions[(cur-1)]->setClientNum(cur);
	minions[(cur-1)]->setHP(100);

	std::string reply = "N:";
	reply += ProtocolParser::numToStr(cur);

	return send(reply, (cur-1), false);
}
void Network::updateOpponents(int cn)
{
	std::string tmp = "X:";
	tmp += ProtocolParser::numToStr(player->getRect()->x);
	tmp += "Y:";
	tmp += ProtocolParser::numToStr(player->getRect()->y);
	tmp += "A:";
	tmp += ProtocolParser::numToStr(player->getAngle());
	send(tmp, (cn-1), true);

	for(int i=0; i<MAX_CLIENTS; i++)
	{
		if((minions[i]->getClientNum() != cn) && (minions[i]->getHP() > 0))
		{
			std::string temp = "P:";
			temp += ProtocolParser::numToStr(minions[i]->getClientNum());
			temp += "X:";
			temp += ProtocolParser::numToStr(minions[i]->getRect()->x);
			temp += "Y:";
			temp += ProtocolParser::numToStr(minions[i]->getRect()->y);
			temp += "A:";
			temp += ProtocolParser::numToStr(minions[i]->getAngle());
			send(temp, (cn-1), false);
		}
	}
}
void Network::removeClient(int cNum)
{
	if(cNum != clientNum)
	{
		std::fstream log("log.txt", std::fstream::out | std::fstream::app);

		int ccn = cNum;
		if(cNum > clientNum)
			ccn--;

		if(minions[ccn]->getClientNum() == cNum)
		{
			log<<"Client #"<<cNum<<": Disconnected."<<std::endl;
			minions[ccn]->setClientNum(-1);
			minions[ccn]->setHP(0);
		}
		else
			log<<"Player at index: "<<ccn<<" is not client #"<<cNum<<"!\n";

		log.close();
	}
}
bool Network::setPlayer(Fighter *f)
{
	if(player == NULL)
	{
		player = f;
		return true;
	}
	else
		return false;
}
bool Network::setSound(Sound *s)
{
	if(sound == NULL)
	{
		sound = s;
		return true;
	}
	else
		return false;
}
bool Network::setPowerup(Powerup* p)
{
	if (powerup==NULL) {
		powerup = p;
		return true;
	}
	else {
		return false;
	}

}
Opponent *Network::getOpponent(int cn)
{
	if(cn == clientNum)
		return NULL;

	if(cn > clientNum)
		return minions[(cn-1)];
	else
		return minions[cn];
}
void Network::close()
{
	SDLNet_UDP_Close(servSock);
	SDLNet_UDP_Close(sendSock);
	servSock = NULL;
	sendSock = NULL;
}
